package cn.epsilon3.service.twoFactorAuthentication.impl;

import cn.epsilon3.common.E3BaseService;
import cn.epsilon3.domain.e3.po.*;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.epsilon3.common.Result;
import cn.epsilon3.constant.Base;
import cn.epsilon3.constant.ResultEnum;
import cn.epsilon3.domain.e3.dto.UserAuthenticatorDTO;
import cn.epsilon3.domain.e3.vo.E3BindEmailVO;
import cn.epsilon3.domain.e3.vo.E3SendEmailVO;
import cn.epsilon3.domain.e3.vo.E3VerifyAuthenticatorCodeVO;
import cn.epsilon3.domain.e3.vo.E3VerifyEmailCodeVO;
import cn.epsilon3.handler.E3RedisKeyHandler;
import cn.epsilon3.configBean.Initializing2FAConfig;
import cn.epsilon3.utils.*;
import cn.epsilon3.handler.E3SendEmailHandler;
import cn.epsilon3.mybatis.service.E3UserAuthenticatorMapperService;
import cn.epsilon3.mybatis.service.E3UserMapperService;
import cn.epsilon3.service.twoFactorAuthentication.E32FAService;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

/**
 * @description 2FA服务
 * @author: epsilon3
 * @date: 2023/8/26 0:03
 */
@Service
public class E32FAServiceImpl extends E3BaseService implements E32FAService {

    @Resource
    private cn.epsilon3.handler.E3SendEmail sendEmail;

    @Resource
    private E3UserMapperService userMapperService;

    @Resource
    private E3UserAuthenticatorMapperService userAuthenticatorMapperService;

    @Resource
    private Initializing2FAConfig initializing2FaConfig;

    @Override
    public Result<String> e3CheckBindEmail() {
        Principal principal = SecurityUtils.principal();
        String userEmail = principal.getUserEmail();
        if (StrUtil.isBlank(userEmail)) {
            return new Result<>(ResultEnum.ERROR, "请先绑定私人邮箱");
        }
        E3SendEmailObj e3SendEmailObj = BeanUtils.getBean(E3SendEmailHandler.class).e3CheckBindEmail();
        E3RedisObj e3RedisObj = BeanUtils.getBean(E3RedisKeyHandler.class).e3BindEmailRedisKey();
        sendEmail.sendEmail(new String[]{userEmail}, e3SendEmailObj, e3RedisObj);
        return new Result<>(ResultEnum.SUCCESS, "邮件发送成功，请前往私人邮箱查看");
    }

    @Override
    public Result<String> e3BindEmail(E3BindEmailVO e3BindEmailVO) {
        Principal principal = SecurityUtils.principal();
        String email = e3BindEmailVO.getEmail();
        String code = e3BindEmailVO.getCode();
        E3RedisObj e3RedisObj = BeanUtils.getBean(E3RedisKeyHandler.class).e3BindEmailRedisKey();
        String redisKey = e3RedisObj.getRedisKey();
        Result<String> result = verifyEmailCode(code, redisKey);
        if (ObjectUtil.isNotNull(result)) {
            return result;
        }
        E3UserPo e3UserPo = new E3UserPo();
        e3UserPo.setId(principal.getId());
        e3UserPo.setUserEmail(email);
        userMapperService.updateOneByUserId(e3UserPo);
        RedisUtils.in().deleteObj(redisKey);
        return new Result<>(ResultEnum.SUCCESS, "私人邮箱绑定成功");
    }

    @Override
    public Result<String> e3SendBindEmail(E3SendEmailVO e3SendEmailVO) {
        String email = e3SendEmailVO.getEmail();
        E3SendEmailObj e3SendEmailObj = BeanUtils.getBean(E3SendEmailHandler.class).e3BindEmail();
        E3RedisObj e3RedisObj = BeanUtils.getBean(E3RedisKeyHandler.class).e3BindEmailRedisKey();
        sendEmail.sendEmail(new String[]{email}, e3SendEmailObj, e3RedisObj);
        return new Result<>(ResultEnum.SUCCESS, "邮件发送成功，请前往私人邮箱查看");
    }

    @Override
    public Result<String> e3VerifyEmailCode(E3VerifyEmailCodeVO e3VerifyEmailCodeVO) {
        String code = e3VerifyEmailCodeVO.getCode();
        E3RedisObj e3RedisObj = BeanUtils.getBean(E3RedisKeyHandler.class).e3VerifyEmailRedisKey();
        String redisKey = e3RedisObj.getRedisKey();
        return ObjectUtil.isNotNull(verifyEmailCode(code, redisKey)) ? verifyEmailCode(code, redisKey) : new Result<>(ResultEnum.SUCCESS);
    }

    @Override
    public Result<Object> e3CheckAuthenticator() {
        AccountInfo accountInfo = SecurityUtils.currentAccount();
        String userName = accountInfo.getPrincipal().getUserName();
        E3UserAuthenticatorPo e3UserAuthenticatorPo = userAuthenticatorMapperService.selectOneByUserId(accountInfo.getPrincipal().getId());
        if (ObjectUtil.isNull(e3UserAuthenticatorPo)) {
            UserAuthenticatorDTO userAuthenticatorDTO = new UserAuthenticatorDTO();
            String secretKey = AuthenticatorUtils.generateRandomKey();
            String uri = AuthenticatorUtils.generateAuthenticatorCode(secretKey, userName, initializing2FaConfig.getIssuer());
            String codeBase64 = WebUtils.generateQRCodeBase64(uri, Base.IMAGE_PNG, initializing2FaConfig.getPngHeight(), initializing2FaConfig.getPngWidth());
            userAuthenticatorDTO.setKey(secretKey);
            userAuthenticatorDTO.setUriBase64(codeBase64);
            Fa fa = accountInfo.getFa();
            fa.setSecretKey(secretKey);
            accountInfo.setFa(fa);
            WebUtils.setSaSessionValue(Base.E3_AUTH_INFO_SESSION, accountInfo);
            return new Result<>(ResultEnum.SUCCESS, userAuthenticatorDTO);
        }
        return new Result<>(ResultEnum.SUCCESS);
    }

    @Override
    public Result<String> e3BindAuthenticator(E3VerifyAuthenticatorCodeVO e3VerifyAuthenticatorCodeVO) {
        String code = e3VerifyAuthenticatorCodeVO.getCode();
        AccountInfo accountInfo = SecurityUtils.currentAccount();
        Fa fa = accountInfo.getFa();
        String secretKey = accountInfo.getFa().getSecretKey();
        if (StrUtil.isBlank(secretKey)) {
            return new Result<>(ResultEnum.ERROR, "绑定验证器失败");
        }
        String generateCode = AuthenticatorUtils.generateCode(secretKey);
        if (!StrUtil.equals(generateCode, code)) {
            return new Result<>(ResultEnum.ERROR, "动态验证码错误");
        }
        E3UserAuthenticatorPo e3UserAuthenticatorPo = new E3UserAuthenticatorPo();
        e3UserAuthenticatorPo.setSecretKey(secretKey);
        e3UserAuthenticatorPo.setUserId(SecurityUtils.currentAccount().getPrincipal().getId());
        e3UserAuthenticatorPo.setEffective(Base.INT1);
        userAuthenticatorMapperService.insertOne(e3UserAuthenticatorPo);
        fa.setEnable(true);
        fa.setSecretKey(secretKey);
        accountInfo.setFa(fa);
        WebUtils.setSaSessionValue(Base.E3_AUTH_INFO_SESSION, accountInfo);
        return new Result<>(ResultEnum.SUCCESS, "绑定成功");
    }

    /**
     * 需要二步验证的操作  校验动态验证码
     *
     * @param code 验证码
     * @return 验证结果
     */
    @Override
    public Result<String> verifyOpe2Fa(String code) {
        Boolean enable = SecurityUtils.currentAccount().getFa().getEnable();
        if (!enable) {
            return new Result<>(ResultEnum.SUCCESS);
        }
        boolean isVerify = (boolean) WebUtils.getSessionValue(Base.E3_VERIFY_OPE_2FA_SESSION);
        if (! isVerify) {
            return new Result<>(ResultEnum.ERROR, "验证器校验未通过");
        }
        AssertUtils.isBlank(code, "动态验证码错误");
        String secretKey = SecurityUtils.currentAccount().getFa().getSecretKey();
        AssertUtils.isBlank(secretKey, "账户未开启双重验证");
        String generateCode = AuthenticatorUtils.generateCode(secretKey);
        if (! StrUtil.equals(generateCode, code)) {
            return new Result<>(ResultEnum.ERROR, "动态验证码错误");
        }
        WebUtils.removeSessionValue(Base.E3_VERIFY_OPE_2FA_SESSION);
        WebUtils.setSessionValue(Base.E3_OPE_2FA_SESSION,true);
        return new Result<>(ResultEnum.SUCCESS);
    }


    private Result<String> verifyEmailCode(String code, String redisKey) {
        if (!RedisUtils.in().hasKey(redisKey)) {
            return new Result<>(ResultEnum.ERROR, "邮件验证已过期");
        }
        String verifyCode = RedisUtils.in().getObjAsStr(redisKey);
        if (!StrUtil.equals(verifyCode, code)) {
            return new Result<>(ResultEnum.ERROR, "邮件验证码错误");
        }
        return null;
    }
}
